﻿# define _CRT_SECURE_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
冒泡排序：

void bubble_sort(int arr[], int sz)
{
	int i = 0;
	//趟
	for (i = 0; i < sz - 1; i++)
	{
		//每一趟冒泡排序的过程
		int j = 0;
		for (j = 0; j < sz-1-i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

void print_arr(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

void test1()
{
	//想排序一组整型数据 - 升序
	int arr[] = { 3,1,5,7,9,2,4,0,8,6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz);
	print_arr(arr, sz);
}



qsort函数的模拟实现
使⽤回调函数，模拟实现qsort（采⽤冒泡的⽅式）。
注意：这⾥第⼀次使⽤ void* 的指针，讲解 void* 的作⽤。
//
int cmp_int(const void* p1, const void* p2)
{
	return *(int*)p1 - *(int*)p2;
}

void test2()
{
	//想排序一组整型数据 - 升序
	int arr[] = { 3,1,5,7,9,2,4,0,8,6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	print_arr(arr, sz);
}

void Swap(char* buf1, char*buf2, size_t width)
{
	int i = 0;
	for (i = 0; i < width; i++) 
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void bubble_sort2(void* base, size_t sz, size_t width, int (*cmp)(const void*p1, const void*p2))
{
	int i = 0;
	//趟
	for (i = 0; i < sz - 1; i++)
	{
		//每一趟冒泡排序的过程
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			//if (arr[j] > arr[j + 1])
			if(cmp((char*)base + j * width, (char*)base + (j + 1) * width)>0)
			{
				/*int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;*/
				//交换
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}

//

void test3()
{
	//设计和实现bubble_sort2(),这个函数能够排序任意类型的数据
	int arr[] = { 3,1,5,7,9,2,4,0,8,6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort2(arr, sz, sizeof(arr[0]), cmp_int);
	print_arr(arr, sz);
}


//使用bubble_sort2函数来排序结构体的数据

struct Stu
{
	char name[20];
	int age;
};

int cmp_stu_by_name(const void* p1, const void*p2)
{
	return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}

int cmp_stu_by_age(const void* p1, const void* p2)
{
	return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}

void test4()
{
	struct Stu arr[] = { {"zhangsan", 18},{"lisi", 35},{"wangwu", 15}};
	int sz = sizeof(arr) / sizeof(arr[0]);
	//bubble_sort2(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
	bubble_sort2(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
	//打印arr数组的内容
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%s %d\n", arr[i].name, arr[i].age);
	}
}


int main()
{
	//test1();
	//test2();
	//test3();
	test4();
	return 0;
}


sizeof和strlen的对⽐
 sizeof在学习操作符的时候，我们学习了 sizeof ， sizeof 计算变量所占
	内存内存空间⼤⼩的，单位是字节，
	如果操作数是类型的话，计算的是使⽤类型创建的变量所占内存空间的⼤⼩。
sizeof 只关注占⽤内存空间的⼤⼩，不在乎内存中存放什么数据。
int main()
{
	//int a = 10;
	//printf("%zd\n", sizeof(a));
	//printf("%zd\n", sizeof(int));
	//printf("%zd\n", sizeof a);//这里可以省略括号
	//printf("%zd\n", sizeof int);//err

	int arr1[4] = { 0 };
	char arr2[4] = { 0 };
	printf("%zd\n", sizeof(arr1));//16
	printf("%zd\n", sizeof(int [4]));//16

	printf("%zd\n", sizeof(arr2));//4
	printf("%zd\n", sizeof(char [4]));//4

	return 0;
}



1 .strlen 是C语⾔库函数，功能是求字符串⻓度。
2.统计的是从 strlen 函数的参数 str 中这个地址开始向后， \0 之前字符串中字符的个数。
strlen 函数会⼀直向后找 \0 字符，直到找到为⽌，所以可能存在越界查找。

int main()
{
	//char arr[20] = "abcdef";
	//size_t len = strlen(arr);//
	//printf("len = %zd\n", len);

	//size_t sz = sizeof(arr);//
	//printf("sz = %zd\n", sz);

	//char arr[] = { 'a', 'b','c' };
	//printf("%zd\n", strlen(arr));//随机的

	//char arr[6] = "abcdef";//err

	return 0;
}

#include <stdio.h>
int main()
{
    char arr1[3] = { 'a', 'b', 'c' };
    char arr2[] = "abc";
 
    printf("%d\n", strlen(arr1));//随机值
    printf("%d\n", strlen(arr2));//3

    printf("%d\n", sizeof(arr1));//3
    printf("%d\n", sizeof(arr2));//4
    return 0;
}




sizeof 和 strlen的对⽐:

sizeof strlen
1. sizeof是操作符
2. sizeof计算操作数所占内存的⼤⼩，单位是字节

strlen
3. 不关注内存中存放什么数据
1. strlen是库函数，使⽤需要包含头⽂件 string.h
2. srtlen是求字符串⻓度的，统计的是 \0 之前字符的隔个数
3. 关注内存中是否有 \0 ，如果没有 \0 ，就会持续往后找，可
能会越界















数组名的理解
数组名一般表示数组首元素的地址
但是有2个例外：
1. sizeof(数组名)，数组名表示整个数组，计算的是整个数组的大小，单位是字节
2. &数组名，数组名表示整个数组，取出的数组的地址
除此之外，所有遇到的数组名都是数组首元素的地址


int main()
{
	int a[] = { 1,2,3,4 };//a数组有4个元素，每个元素是int类型的数据

	printf("%zd\n", sizeof(a));//16 - sizeof(数组名)的情况，计算的是整个数组的大小，单位是字节 - 16
	printf("%zd\n", sizeof(a + 0));//a表示的就是数组首元素的地址,a+0还是首元素的地址 - 4/8
	//int*
	printf("%zd\n", sizeof(*a));//a表示的就是数组首元素的地址,*a 就是首元素，大小就是4个字节
	printf("%zd\n", sizeof(a + 1));//a表示的就是数组首元素的地址，a+1就是第二个元素的地址，这里的计算的是第二个元素的地址的大小-4/8

	printf("%zd\n", sizeof(a[1]));//a[1]是数组的第二个元素，大小是4个字节
	printf("%zd\n", sizeof(&a));//&a - 取出的是数组的地址，但是数组的地址也是地址，是地址，大小就是4/8个字节
	//int (*pa)[4] = &a
	//int(*)[4]
	printf("%zd\n", sizeof(*&a));//sizeof(a) -16
	//1. & 和 * 抵消
	//2.&a 的类型是数组指针，int(*)[4]，*&a就是对数组指针解引用访问一个数组的大小，是16个字节

	printf("%zd\n", sizeof(&a + 1));//&a+1是跳过整个数组后的一个地址，是地址，大小就是4/8个字节

	printf("%zd\n", sizeof(&a[0]));//&a[0]是数组第一个元素的地址，大小就是4/8个字节
	printf("%zd\n", sizeof(&a[0] + 1));//&a[0] + 1 是第二个元素的地址，大小就是4/8个字节
	                      //int*
	return 0;
}



int main()
{
	char arr[] = { 'a','b','c','d','e','f' };//arr数组中有6个元素

	printf("%d\n", sizeof(arr));//计算的是整个数组的大小，6个字节
	printf("%d\n", sizeof(arr + 0));//arr+0 是数组第一个元素的地址 4/8
	printf("%d\n", sizeof(*arr));//*arr是首元素，计算的是首元素的大小，就是1个字节
	printf("%d\n", sizeof(arr[1]));//arr[1]  - 1
	printf("%d\n", sizeof(&arr));//4/8
	printf("%d\n", sizeof(&arr + 1));//4/8
	printf("%d\n", sizeof(&arr[0] + 1));//4/8

	return 0;
}



int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", strlen(arr));//随机值
	printf("%d\n", strlen(arr + 0));//随机值
						 'a'-97
	printf("%d\n", strlen(*arr));//err
	                   //'b'-98
	printf("%d\n", strlen(arr[1]));//err
	printf("%d\n", strlen(&arr));//随机值
	printf("%d\n", strlen(&arr + 1));//随机值
	printf("%d\n", strlen(&arr[0] + 1));//随机值

	return 0;
}





